package com.fdb.efp.flow.service;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.fdb.efp.flow.common.constance.FlowConstance;
import com.fdb.efp.flow.common.enumeration.NodeExecuteType;
import com.fdb.efp.flow.common.vo.EngineVO;
import com.fdb.efp.flow.common.vo.PageApproveVO;
import com.fdb.efp.flow.executor.FlowExecutorFactory;
import com.fdb.efp.flow.service.facade.BizEventInfoService;
import com.fdb.efp.flow.service.facade.BizFlowInfoService;
import com.fdb.efp.flow.service.facade.BizFlowRouteService;
import com.fdb.efp.flow.service.facade.InstFlowEventService;
import com.fdb.efp.flow.service.facade.InstFlowInfoService;
import com.fdb.efp.flow.service.facade.InstFlowProcessService;
import com.fdb.efp.flow.service.vo.BizEventInfoVO;
import com.fdb.efp.flow.service.vo.BizFlowInfoVO;
import com.fdb.efp.flow.service.vo.InstFlowEventVO;
import com.fdb.efp.flow.service.vo.InstFlowInfoVO;
import com.fdb.basic.framework.core.cache.CacheManager;
import com.fdb.basic.framework.core.constant.CacheConstant;
import com.fdb.basic.framework.core.exception.FlowException;
import com.fdb.basic.framework.core.util.StringUtil;
import com.fdb.basic.framework.core.util.TimeUtil;
import com.fdb.basic.framework.core.util.UUIDUtil;
import com.fdb.basic.framework.core.vo.OrgInfoCacheVO;
import com.fdb.basic.framework.core.vo.PluginVO;

/**
 * 
 * 业务流程信息
 * 
 * @author Cytus_
 * @since 2018年5月7日 上午10:27:19
 * @version 1.0
 *
 */
@Service("bizFlowEngine")
public class BizFlowEngine implements FlowEngine {
	
	private static Logger logger = LoggerFactory.getLogger(BizFlowEngine.class);
	
	@Autowired
	@Qualifier("instFlowInfoService")
	private InstFlowInfoService instFlowInfoService;
	
	@Autowired
	@Qualifier("bizEventInfoService")
	private BizEventInfoService bizEventInfoService;
	
	@Autowired
	@Qualifier("bizFlowInfoService")
	private BizFlowInfoService bizFlowInfoService;
	
	@Autowired
	@Qualifier("instFlowEventService")
	private InstFlowEventService instFlowEventService;
	
	@Autowired
	@Qualifier("instFlowProcessService")
	private InstFlowProcessService instFlowProcessService;
	
	@Autowired
	@Qualifier("bizFlowRouteService")
	private BizFlowRouteService bizFlowRouteService;
	
	@Autowired
	@Qualifier("flowExecutorFactory")
	private FlowExecutorFactory factory;

	private String initBizFlow(String flowId, String flowInstanceId, String bizLegalOrgCode)  throws Exception {
		if (StringUtil.isEmpty(flowInstanceId)) return null;
		InstFlowInfoVO vo = instFlowInfoService.queryByFlowInstanceId(flowInstanceId);
		if (Objects.nonNull(vo)) {
			if (FlowConstance.FLOW_STATUS_END.equals(vo.getInstFlowState()) || FlowConstance.FlOW_STATUS_TERMINATE.equals(vo.getInstFlowState())) {
				throw new FlowException("流程实例："+ flowInstanceId +", 当前流程已终止!");
			}
			return flowInstanceId;
		}
		BizFlowInfoVO bizFlowInfo = getBizFlowInfo(flowId);
		if (Objects.isNull(bizFlowInfo)) {
			throw new FlowException("流程ID:"+ flowId +"未找到对应配置信息!");
		}
		InstFlowInfoVO instFlowInfo = new InstFlowInfoVO();
		instFlowInfo.setInstFlowId(flowInstanceId);
		instFlowInfo.setBizFlowId(bizFlowInfo.getBizFlowId());
		instFlowInfo.setBizFlowName(bizFlowInfo.getBizFlowName());
		instFlowInfo.setStartTime(TimeUtil.getCurrentDateTime());
		instFlowInfo.setInstFlowState(FlowConstance.FLOW_STATUS_RUNNING);
		// TODO 机构信息
		instFlowInfo.setBizLegalOrgCode(bizLegalOrgCode);
		// TODO 机构信息
		OrgInfoCacheVO cacheVo = CacheManager.getCacheObject(CacheConstant.CACHE_KEY_ORG_INFOS, bizLegalOrgCode);
		if (Objects.nonNull(cacheVo)) {
			instFlowInfo.setBizLegalOrgCode(cacheVo.getLegalOrgCode());
		}
		try {
			instFlowInfoService.insertInstFlowInfo(instFlowInfo);
			String instFLowId = instFlowInfo.getInstFlowId();
			logger.info("流程ID:"+ flowId +"实例化结束,实例化编号为:"+ instFLowId);
			return instFLowId;
		} catch (Exception e) {
			logger.error("流程ID:"+ flowId +"实例化出现异常!", e);
			throw e;
		}
	}

	/**
	 * 初始化BIZ FLOW
	 */
	public String initBizFlow(String flowId, String bizLegalOrgCode)  throws Exception {
		BizFlowInfoVO bizFlowInfo = getBizFlowInfo(flowId);
		if (Objects.isNull(bizFlowInfo)) {
			throw new FlowException("流程ID:"+ flowId +"未找到对应配置信息!");
		}
		InstFlowInfoVO instFlowInfo = new InstFlowInfoVO();
		instFlowInfo.setInstFlowId(UUIDUtil.getUUID());
		instFlowInfo.setBizFlowId(bizFlowInfo.getBizFlowId());
		instFlowInfo.setBizFlowName(bizFlowInfo.getBizFlowName());
		instFlowInfo.setStartTime(TimeUtil.getCurrentDateTime());
		instFlowInfo.setInstFlowState(FlowConstance.FLOW_STATUS_RUNNING);
		instFlowInfo.setBizLegalOrgCode(bizLegalOrgCode);
		try {
			instFlowInfoService.insertInstFlowInfo(instFlowInfo);
			String instFLowId = instFlowInfo.getInstFlowId();
			logger.info("流程ID:"+ flowId +"实例化结束,实例化编号为:"+ instFLowId);
			return instFLowId;
		} catch (Exception e) {
			logger.error("流程ID:"+ flowId +"实例化出现异常!", e);
			throw e;
		}
	}

	/**
	 * 初始化事件
	 */
	private String initEvent(String flowId, String eventId, String g_bizSerno, String e_bizSerno, String bizLegalOrgCode)  throws Exception {
		if (StringUtil.isEmpty(flowId) || StringUtil.isEmpty(eventId)) {
			logger.error("尚未传入流程ID或事件ID!");
			throw new FlowException("尚未传入流程ID或事件ID!");
		}
		// 判断是否为业务流转过程是否存在
		assertEventInfo(flowId, eventId);
		// 判断事件是否存在
		BizEventInfoVO bizEventInfo = getBizEventInfo(flowId, eventId);
		if (Objects.isNull(bizEventInfo)) {
			logger.error("事件ID:"+ eventId +"未找到对应配置信息!");
			throw new FlowException("事件ID:"+ eventId +"未找到对应配置信息!");
		}
		//
		InstFlowEventVO instFlowEvent = new InstFlowEventVO();
		instFlowEvent.setBizSerno(e_bizSerno);
		instFlowEvent.setInstFlowId(g_bizSerno);
		instFlowEvent.setBizEventId(eventId);
		instFlowEvent.setBizEventName(bizEventInfo.getBizEventName());
		instFlowEvent.setBizStageId(bizEventInfo.getBizStageId());
		instFlowEvent.setBizStageName(bizEventInfo.getBizStageName());
		instFlowEvent.setInstNodeId(null);
		instFlowEvent.setBizEventState(FlowConstance.EVENT_STATUS_READY);
		instFlowEvent.setStartTime(TimeUtil.getCurrentDateTime());
		// TODO 法人
		instFlowEvent.setBizLegalOrgCode(bizLegalOrgCode);
		try {
			instFlowEventService.insertInstFlowEvent(instFlowEvent);
			logger.info("流程ID:"+ flowId +", 事件ID:"+ eventId +"实例化结束, 实例编号为："+ e_bizSerno); 
		} catch (Exception e) {
			
		}
		return e_bizSerno;
	}
	
	/**
	 * 启动事件
	 */
	public Object startEvent(String flowId, String eventId, String g_bizSerno, String e_bizSerno, String bizLegalOrgCode, Object paramObject, boolean isSync) throws Exception {
		// 初始化事件
		e_bizSerno = this.initEvent(flowId, eventId, g_bizSerno, e_bizSerno, bizLegalOrgCode);
		// 初始化流程
		initBizFlow(flowId, g_bizSerno, bizLegalOrgCode);
		EngineVO evo = new EngineVO();
		evo.setFlowId(flowId);
		evo.setBizSerno(e_bizSerno);
		evo.setEventId(eventId);
		evo.setEventInstanceId(e_bizSerno);
		PluginVO pluginVo = setArgs2PluginVO(paramObject);
		evo.setPluginVo(pluginVo);
		evo.getPluginVo().addVOs(PluginVO.E_BIZ_SERNO_KEY, e_bizSerno);
		evo.getPluginVo().addVOs(PluginVO.G_BIZ_SERNO_KEY, g_bizSerno);
		evo.setGlobalBizSerno(g_bizSerno);
		evo.setFlowInstanceId(g_bizSerno);
		setFlowId(evo);
		if (isSync) {
			EngineVO vo = factory.syncExecuteEvent(evo);
			return vo.getPluginVo().getVo(FlowConstance.LAST_PLUGIN_OUT_PARAM_KEY);
		} else {
			factory.asyncExecuteEvent(evo);
			return e_bizSerno;
		}
	}
	
	/**
	 * 判断是否存在
	 * @param flowId
	 * @param eventId
	 * @throws FlowException
	 */
	private void assertEventInfo(String flowId, String eventId) throws FlowException {
		int num = bizEventInfoService.countByFlowIdAndEventId(flowId, eventId);
		if (num < 1) {
			throw new FlowException("传入的流程ID:"+ flowId +",事件ID："+ eventId +"未配置!");
		}
	}
	
	
	/**
	 * 继续当前流程
	 */
	public Object continueEvent(String g_bizSerno, String e_bizSerno, String eventId, PageApproveVO pageVo, Object paramObject, boolean isSync) throws Exception {
		EngineVO evo = new EngineVO();
		evo.setBizSerno(e_bizSerno);
		evo.setEventId(eventId);
		evo.setEventInstanceId(e_bizSerno);
		evo.setGlobalBizSerno(g_bizSerno);
		evo.setFlowInstanceId(g_bizSerno);
		PluginVO pluginVo = setArgs2PluginVO(paramObject);
		evo.setPluginVo(pluginVo);
		evo.addExeParams(FlowConstance.PAGE_PLUGIN_VO_KEY2, pageVo);
		evo.getPluginVo().addVOs(PluginVO.E_BIZ_SERNO_KEY, e_bizSerno);
		evo.getPluginVo().addVOs(PluginVO.G_BIZ_SERNO_KEY, g_bizSerno);
		setFlowId(evo);
		if (isSync) {
			EngineVO vo = factory.syncExecuteEvent(evo);
			return vo.getPluginVo().getVo(FlowConstance.LAST_PLUGIN_OUT_PARAM_KEY);
		} else {
			factory.asyncExecuteEvent(evo);
			return null;
		} 
	}
	
	/**
	 * 设置参数到pluginVO中
	 * @param paramObject
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private PluginVO setArgs2PluginVO(Object paramObject) {
		PluginVO pluginVo = new PluginVO();
		if (Objects.nonNull(paramObject) && StringUtil.isNotEmpty(StringUtil.replaceObjNull(paramObject))) {
    		if (paramObject instanceof Map) {
    		    Map<String, Object> dataMap = removeNullValue((Map<String, Object>) paramObject);
    			pluginVo.getVos().putAll(dataMap);
    			pluginVo.getFlowInVo().putAll(dataMap);
    			pluginVo.addVOs(FlowConstance.PAGE_PLUGIN_VO_KEY, paramObject);
    		} else {
    			pluginVo.addVOs(FlowConstance.PAGE_PLUGIN_VO_KEY, paramObject);
    		}
		}
		if(pluginVo.getVos().get(FlowConstance.PAGE_PLUGIN_VO_KEY2) == null){
			pluginVo.addVOs(FlowConstance.PAGE_PLUGIN_VO_KEY2, new PageApproveVO());
		}
		return pluginVo;
	}
	
	/**
	 * 去除map中值为空的记录
	 * @param paramObject
	 * @return
	 */
	private static Map<String, Object> removeNullValue(Map<String, Object> paramObject) {
	    Map<String, Object> dataMap = new HashMap<String, Object>();
	    if (Objects.nonNull(paramObject) && !paramObject.isEmpty()) {
	       paramObject.entrySet().parallelStream().filter(s -> isNotEmpty(s.getValue())).forEach(s -> dataMap.put(s.getKey(), s.getValue()));
	    }
	    return dataMap;
	}
	
	/**
	 * 判断OBJECT是否为空
	 * @param obj
	 * @return
	 */
	private static boolean isNotEmpty(Object obj) {
	    return Objects.nonNull(obj) && StringUtil.isNotEmpty(String.valueOf(obj));
	}
	
	/**
	 * 查询流程信息
	 * @param flowId
	 * @return
	 */
	private BizFlowInfoVO getBizFlowInfo(String flowId) {
		BizFlowInfoVO queryInfo = new BizFlowInfoVO();
		queryInfo.setBizFlowId(flowId);
		return bizFlowInfoService.queryByPk(queryInfo);
	}
	
	
	private BizEventInfoVO getBizEventInfo(String flowId, String eventId) {
		BizEventInfoVO queryInfo = new BizEventInfoVO();
		queryInfo.setBizEventId(eventId);
		return bizEventInfoService.queryByPk(queryInfo);
	}

	/**
	 * 执行事件，完成后将下一事件修改为暂停状态，主要用于页面插件提交
	 * @param g_bizSerno 全局流水号
	 * @param e_bizSerno 业务流水号
	 * @param eventId 事件ID
	 * @param instNodeId 实例化节点ID
	 * @param taskId 任务ID
	 * @param object 传入参数
	 * @return 执行是否成功
	 * @throws Exception
	 */
	public boolean executeTask(String g_bizSerno, String e_bizSerno, String eventId, String instNodeId, String taskId, Object object) throws Exception {
		boolean result = false;
		EngineVO evo = new EngineVO();
		evo.setBizSerno(e_bizSerno);
		evo.setEventId(eventId);
		evo.setEventInstanceId(e_bizSerno);
		evo.setGlobalBizSerno(g_bizSerno);
		evo.setFlowInstanceId(g_bizSerno);
		PluginVO pluginVo = setArgs2PluginVO(object);
		evo.setPluginVo(pluginVo);
		evo.setEventInstanceId(instNodeId);
		evo.setTaskId(taskId);
		evo.getPluginVo().addVOs(PluginVO.E_BIZ_SERNO_KEY, e_bizSerno);
		evo.getPluginVo().addVOs(PluginVO.G_BIZ_SERNO_KEY, g_bizSerno);
		setFlowId(evo);
		try {
			evo.setNodeExecuteType(NodeExecuteType.PAGE_SUBMIT);
			factory.syncExecuteEvent(evo);
			result = true;
		} catch (Exception e) {
			logger.error("实例化事件ID："+ e_bizSerno +"页面信息提交执行任务出现异常!");
		}
		return result;
	}
	
	private void setFlowId(EngineVO evo) {
	    if (StringUtil.isNotEmpty(evo.getGlobalBizSerno())) {
    		InstFlowInfoVO vo = instFlowInfoService.queryByFlowInstanceId(evo.getGlobalBizSerno());
    		if (Objects.nonNull(vo)) 
    			evo.setFlowId(vo.getBizFlowId());
	    } else {
	        InstFlowEventVO vo = instFlowEventService.queryByBizSerno(evo.getBizSerno());
	        if (Objects.nonNull(vo)) {
	            evo.setGlobalBizSerno(vo.getInstFlowId());
	            evo.setFlowInstanceId(vo.getInstFlowId());
	            InstFlowInfoVO flowVo = instFlowInfoService.queryByFlowInstanceId(evo.getGlobalBizSerno());
	            if (Objects.nonNull(flowVo)) 
	                evo.setFlowId(flowVo.getBizFlowId());
	        }
	    }
	}
	
	
}
